/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.ui;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

import edu.cmu.ri.createlab.jj.blocks.*;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.*;
import edu.cmu.ri.createlab.jj.projects.*;
import edu.cmu.ri.createlab.jj.shapes.RefShape;
import edu.cmu.ri.createlab.jj.visitors.*;
import static edu.cmu.ri.createlab.jj.shapes.ShapeDefs.*;

public class JJDemoPanel extends JLayeredPane implements JJPanelInterface, MouseListener, MouseMotionListener
{
   static final private int MAX_TABS = 6;
   private BVDrawer drawer;
   private BVJavaWriter jwriter;
   private JJProject jjproj;
   private LinkedList<RefBlock> dragBlocks;
   private LinkedList<RefShape> dragShapes;
   private double scale;
   private boolean mouseDrag;
   private int tabs;
   private Dimension d1;
   private Dimension d2;
   private Dimension d3;
   private Runtime rt;
   private Process p1;
   private Process p2;
   private Thread pThread;
   private JJRunProject runP;
   private boolean error;
   
   public JJDemoPanel()
   {
      drawer = new BVDrawer();
      jwriter = new BVJavaWriter();
      dragShapes = null;
      error = false;
      tabs = 0;
      scale = 0.75;
      mainSplitPane = new javax.swing.JSplitPane();
      workScrollPane = new javax.swing.JScrollPane();
      paletteScrollPanes = new javax.swing.JScrollPane[MAX_TABS];
      paletteTabPane = new javax.swing.JTabbedPane();
      toolbar = new javax.swing.JToolBar();
      compileButton = new javax.swing.JButton();
      runButton = new javax.swing.JButton();
      wp = new WorkPanel(drawer);
      pps = new PalettePanel[MAX_TABS];//("P1", drawer);
      dl = new JJDragLabel(scale, drawer);
      codeDisplay = new javax.swing.JTextArea();
      rightSplitPane = new javax.swing.JSplitPane();
      codeScrollPane = new javax.swing.JScrollPane();
      //compileError = new javax.swing.JButton(new javax.swing.ImageIcon(drawer.getErrIcon()));
      compileError = new javax.swing.JButton();
      compileError.setIcon(new javax.swing.ImageIcon(drawer.getErrIcon()));
      //compileError.setText("Compilation Error");
      //compileError.setName("Compilation Error");
      
      Runtime.getRuntime().addShutdownHook(new Thread() {
         public void run() {
             endApp();
         }
     });;

      d1 = new Dimension(500,600);
      d2 = new Dimension(300,600);
      d3 = new Dimension(800,600);
      
      compileButton.setText("Compile");
      compileButton.setName("Compile");
      runButton.setText("Run");
      runButton.setName("Run");
      
      compileButton.addMouseListener(new java.awt.event.MouseAdapter() {
         public void mouseClicked(java.awt.event.MouseEvent evt) {
             compileButtonMouseClicked(evt);
         }
      });
      
      runButton.addMouseListener(new java.awt.event.MouseAdapter() {
         public void mouseClicked(java.awt.event.MouseEvent evt) {
             runButtonMouseClicked(evt);
         }
      });
      
      this.addMouseListener(this);
      this.addMouseMotionListener(this);
      wp.addMouseListener(this);
      wp.addMouseMotionListener(this);
      //pp.addMouseListener(this);
      //pp.addMouseMotionListener(this);
      
      wp.addJJDragReadyListener(new JJDragReadyListener() {
         public void jjDragReadyOccurred(JJDragReadyEvent evt) {
            JJDragReady((JJPanelInterface)evt.getSource());
         }
      });
      
//      pp.addJJDragReadyListener(new JJDragReadyListener() {
//         public void jjDragReadyOccurred(JJDragReadyEvent evt) {
//            JJDragReady((JJPanelInterface)evt.getSource());
//         }
//      });
      
      toolbar.setRollover(true);
      toolbar.add(compileButton);
      toolbar.add(runButton);
      
      wp.setPreferredSize(d1);
      //pp.setPreferredSize(d2);
      workScrollPane.setViewportView(wp);
      workScrollPane.setPreferredSize(d1);
      //paletteScrollPane.setViewportView(pp);
      //paletteScrollPane.setPreferredSize(d2);
      
      paletteTabPane.setPreferredSize(d2);
      //paletteTabPane.addTab("demo", paletteScrollPane);
      
      codeScrollPane.setPreferredSize(d2);
      codeDisplay.setPreferredSize(d2);
      codeDisplay.setEditable(false);
      codeScrollPane.setViewportView(codeDisplay);
      rightSplitPane.setLeftComponent(workScrollPane);
      rightSplitPane.setRightComponent(codeScrollPane);
      rightSplitPane.setPreferredSize(d3);
      rightSplitPane.setDividerLocation(420);
      rightSplitPane.setResizeWeight(1);
      
      //mainSplitPane.setPreferredSize(new Dimension(840,600));
      mainSplitPane.setDividerLocation(320);
      mainSplitPane.setLeftComponent(paletteTabPane);
      mainSplitPane.setRightComponent(rightSplitPane);

      //javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(this);
      javax.swing.BoxLayout mainPanelLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
      this.setLayout(mainPanelLayout);
      
      toolbar.setAlignmentX(0);
      mainSplitPane.setAlignmentX(0);
      this.add(toolbar);
      this.add(mainSplitPane);
//      mainPanelLayout.setHorizontalGroup(
//            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
//            .addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 840, Short.MAX_VALUE)
//            .addComponent(toolbar)
//        );
//      mainPanelLayout.setVerticalGroup(
//            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
//            .addGroup(mainPanelLayout.createSequentialGroup()
//                .addComponent(toolbar)
//                .addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE))
//        );
      
      this.setName("JubJub");
      this.setPreferredSize(new Dimension(1024,600));
      //this.add(mainSplitPane);
   }

   public LinkedList<RefBlock> getDragBlocks()
   {
      return dragBlocks;
   }
   
   protected void setDragBlocks(LinkedList<RefBlock> blocks)
   {
      dragBlocks = blocks;
      dl.setBlocks(dragBlocks);
   }
   
   private void JJDragReady(JJPanelInterface panel)
   {
      System.out.println("DragReady");
      dragBlocks = panel.getDragBlocks();
      dl.setBlocks(dragBlocks);
      jwriter.clear();
      wp.getBlock().accept(jwriter);
      codeDisplay.setText(jwriter.getCode());
      //generateDragShapes();
   }
   
   protected void generateDragShapes()
   {
      if(dragBlocks != null)
      {
         dragShapes = new LinkedList<RefShape>();
         int tempX = 1000;
         int tempY = 0;
         Iterator<RefBlock> it = dragBlocks.iterator();
         while(it.hasNext())
         {
            RefBlock tempBlock = it.next();
            RefShape newShape = new RefShape(tempX, tempY, tempBlock, scale, this);
            tempY = newShape.getBot();
            dragShapes.add(newShape);
         }
      }
   }
   
   public void setProject(JJProject proj)
   {
      jjproj = proj;
   }
   
   public JJProject getProject()
   {
      return jjproj;
   }
   
   public void addTab(JJTab tab)
   {
      //pp.setTab(tab);
      if(tabs >= MAX_TABS) return;
      pps[tabs] = new PalettePanel(tab.getName(), drawer);
      pps[tabs].setTab(tab);
      pps[tabs].addMouseListener(this);
      pps[tabs].addMouseMotionListener(this);
      
      pps[tabs].addJJDragReadyListener(new JJDragReadyListener() {
          public void jjDragReadyOccurred(JJDragReadyEvent evt) {
             JJDragReady((JJPanelInterface)evt.getSource());
          }
      });
      
      paletteScrollPanes[tabs] = new javax.swing.JScrollPane();
      paletteScrollPanes[tabs].setViewportView(pps[tabs]);
      paletteScrollPanes[tabs].setPreferredSize(d2);
      
      paletteTabPane.add(tab.getName(), paletteScrollPanes[tabs]);
      tabs++;
   }
   
   public void remTab(JJTab tab)
   {
      
   }
   
   public void endApp()
   {
      try
      {
         runP.end();
         p1.destroy();
         p2.destroy();
      }
      catch(Exception e)
      {
         
      }
   }
   
   public void setCurrBlock(BlockBlock block)
   {
      wp.setBlock(block);
   }
   
   public BlockVisitorInterface[] getVisitors()
   {
      // TODO Auto-generated method stub
      return null;
   }
   
   public void paintComponent(Graphics g)
   {
      super.paintComponent(g);
      ((Graphics2D)g).setRenderingHint(
            RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
      compileError.paintComponents(g);
      if(mouseDrag && dragShapes != null)
      {
         Iterator<RefShape> it = dragShapes.iterator();
         while(it.hasNext())
            it.next().draw(g, this, drawer);
      }
   }
   
   private void compileButtonMouseClicked(java.awt.event.MouseEvent evt) 
   {

      jwriter.clear();
      wp.getBlock().accept(jwriter);
      codeDisplay.setText(jwriter.getCode());
      jwriter.clear();
      jjproj.accept(jwriter);
   }
   
   private void runButtonMouseClicked(java.awt.event.MouseEvent evt) 
   {
      compileButtonMouseClicked(evt);
      //return;
      //TODO uncomment and delete return
      if(pThread != null && pThread.getState() != Thread.State.TERMINATED) return;
      try 
      {
         rt = Runtime.getRuntime();
         p1 = rt.exec("javac -classpath ;finch.jar *.java finch/*.java");
         BufferedReader input = new BufferedReader(new InputStreamReader(p1.getInputStream()));
         BufferedReader errOut = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
         
         String line=null;
         
         while((line=errOut.readLine()) != null)
         {
            System.out.println(line);
         }
         
         while((line=input.readLine()) != null) {
             System.out.println(line);
         }

         int exitVal = p1.waitFor();
         System.out.println("Exited with error code "+exitVal);
         if(exitVal != 0)
         {
            //TODO display error message and return'
            System.out.println("Compilation Error!");
            toolbar.add(compileError);
            compileError.setVisible(true);
            toolbar.repaint();
            //Thread.sleep(2000);
            //toolbar.remove(compileError);
            this.repaint();
            error = true;
            return;
         }
         
         //String currDir = System.getProperty("user.dir");
         //String javaDir = "c:\\Program Files\\Java\\jdk1.6.0_13\\bin\\";
         
         runP = new JJRunProject(jjproj.getName());
         pThread = new Thread(runP);
         pThread.start();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }

   
   public void mouseClicked(MouseEvent arg0)
   {
   }

   
   public void mouseEntered(MouseEvent arg0)
   {
      // TODO Auto-generated method stub
      
   }

   
   public void mouseExited(MouseEvent arg0)
   {
      // TODO Auto-generated method stub
      
   }

   
   public void mousePressed(MouseEvent arg0)
   {
      // TODO Auto-generated method stub
      mouseDrag = true;
      System.out.println("Press");
      this.add(dl, JLayeredPane.DRAG_LAYER);
      Point mouseP = this.getMousePosition();
      dl.setBounds((int)mouseP.getX(), (int)mouseP.getY(), dl.getWidth(), dl.getHeight());
      if(error)toolbar.remove(compileError);
      error = false;
   }

   
   public void mouseReleased(MouseEvent arg0)
   {
      mouseDrag = false;
      Point p = this.getMousePosition(true);
      int currx = (int)(MouseInfo.getPointerInfo().getLocation().x -rightSplitPane.getLocationOnScreen().x);
      int curry = (int)(MouseInfo.getPointerInfo().getLocation().y -rightSplitPane.getLocationOnScreen().y);
      //finding the mouse position relative to a sub-component is non-trivial when
      //it doesn't recognize that the mouse is above that component.
      if(p != null 
            && workScrollPane.getBounds().contains(currx, curry) 
            && dragBlocks != null) 
      {
         wp.addBlocks(dragBlocks, 
               (int)(MouseInfo.getPointerInfo().getLocation().x -wp.getLocationOnScreen().x), 
               (int)(MouseInfo.getPointerInfo().getLocation().y -wp.getLocationOnScreen().y)
               );
         jwriter.clear();
         wp.getBlock().accept(jwriter);
         codeDisplay.setText(jwriter.getCode());
      }
      dragBlocks = null;
      dl.setBlocks(null);
      this.remove(dl);
      //workScrollPane.setViewportView(wp);
      this.repaint();
      Point mouseP = this.getMousePosition();
      if(wp.contains((int)mouseP.getX(), (int)mouseP.getY()))
         wp.mouseReleased(arg0);
   }

   
   public void mouseDragged(MouseEvent arg0)
   {
      //System.out.println("Dragging");
      if(dragBlocks != null)
      {
         Point mouseP = this.getMousePosition();
         if(mouseP != null)
            dl.setBounds((int)mouseP.getX(), (int)mouseP.getY(), 
                  dl.getWidth(), dl.getHeight());
         if(dragBlocks.getFirst().getBlock().getType() == BlockTypes.VAR ||
               (dragBlocks.getFirst().getBlock().getType() == BlockTypes.EXPRESSION
                     && ((ExpressionBlock)dragBlocks.getFirst().getBlock()).getEType() != ExpressTypes.ASSIGN))
         {
            wp.setDYType(DY_VAR);
         }
         else wp.setDYType(DY_CODE);
         if(wp.contains((int)mouseP.getX(), (int)mouseP.getY()))
            wp.mouseDragged(arg0);
      }
//      if(dragShapes != null)
//      {
//         int tempX = arg0.getX();
//         int tempY = arg0.getY();
//         Iterator<RefShape> it = dragShapes.iterator();
//         while(it.hasNext())
//         {
//            RefShape tempShape = it.next();
//            tempShape.setOrigin(tempX, tempY);
//            tempY = tempShape.getBot();
//         }
//         this.repaint();
//      }
      
   }

   
   public void mouseMoved(MouseEvent arg0)
   {
      // TODO Auto-generated method stub
      
   }
   
   private javax.swing.JSplitPane mainSplitPane;
   private javax.swing.JSplitPane rightSplitPane;
   private javax.swing.JScrollPane workScrollPane;
   private javax.swing.JScrollPane codeScrollPane;
   private WorkPanel wp;
   private javax.swing.JTabbedPane paletteTabPane;
   //todo change palette to arraylists
   private javax.swing.JScrollPane[] paletteScrollPanes;
   private PalettePanel[] pps;
   private javax.swing.JToolBar toolbar;
   private javax.swing.JButton compileButton;
   private javax.swing.JButton runButton;
   private JJDragLabel dl;
   private javax.swing.JButton compileError;
   private javax.swing.JTextArea codeDisplay;
   
   public static void main(String[] args)
   {
      JJDemoPanel testJJ = new JJDemoPanel();
      JJProject jjproj = JJProject.getTestProject();
      LinkedList<RefBlock> dragTest = new LinkedList<RefBlock>();
      
      testJJ.setProject(jjproj);
      testJJ.addTab(jjproj.getTabs().get(0));
      testJJ.setCurrBlock((BlockBlock)(jjproj.getClasses().get(0).getMethodBlocks().get(1)));
      RefBlock dragBlock = ((BlockBlock)jjproj.getClasses().get(0).getMethodBlocks().get(1)).getCode().get(3);

      dragTest.add(dragBlock);
      
      //testJJ.setDragBlocks(dragTest);
      testJJ.generateDragShapes();
      
      JFrame frame = new JFrame("JubJub Demo");
      
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(testJJ);
      
      frame.pack();
      
      frame.setVisible(true);
   }
}
